package com.sunrise.xdoc.helper;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.StringUtils;

public class OracleHelper {

	private static Log logger = LogFactory.getLog(OracleHelper.class);

	/**
	 * The following method assumes the exception message contains the
	 * following: "ORA-00001: unique constraint (schema.index_name)
	 * violated". Independently of the language, it looks for the pattern "
	 * ORA-00001" and returns the second string "index_name".
	 */
	public static String searchViolatedUniqueConstraint(Throwable th) {
		String result = searchConstraint(th, "ORA-00001");
		if (null == result) {
			// this allows to have triggers that simulate
			// unique constraints accross several tables
			result = searchConstraint(th, "ORA-20001");
		}
		return result;
	}

	public static String searchViolatedDeleteConstraint(Throwable th) {
		return searchConstraint(th, "ORA-02292");
	}

	public static String searchViolatedCheckConstraint(Throwable th) {
		String result = searchConstraint(th, "ORA-02290");
		if (null == result) {
			// this allows to have triggers that simluate
			// chek constraints over several tables
			result = searchConstraint(th, "ORA-20290");
		}
		return result;
	}

	private static String searchConstraint(Throwable th, String constraintType) {
		String msg = th.getMessage();
		if (msg.contains(constraintType)) {
			try {
				// Get the message of the root error.
				msg = msg.substring(msg.indexOf(constraintType) + constraintType.length());
				// Look for the string between parentheses
				int beginIndex = msg.indexOf('(');
				int endIndex = msg.indexOf(')');
				msg = msg.substring(beginIndex + 1, endIndex);
				// Look for the second string after '.'
				beginIndex = msg.indexOf('.');
				return msg.substring(beginIndex + 1);
			} catch (RuntimeException e) {
				// if any exception occurs, we stop searching
				logger.warn("Unable to identity the violated constraint in the exception: " + th.getMessage());
				return null;
			}
		} else {
			// Look into the cause, if any
			Throwable cause = th.getCause();
			if (cause != null) {
				return searchConstraint(cause, constraintType);
			} else {
				return null;
			}
		}
	}

	/**
	 * Look for the oracle error message in the exceptions, return null if not
	 * found.
	 * 
	 * @param th
	 *            Base exception, can contain nested exceptions
	 * @return oracle error message, null if none is found.
	 */
	public static String searchOracleErrorMessage(Throwable th) {
		String msg = th.getMessage();
		if (msg.contains("ORA-")) {
			return msg;
		} else {
			// Look into the cause, if any
			if (th.getCause() != null) {
				return searchOracleErrorMessage(th.getCause());
			} else {
				return null;
			}
		}
	}

	/**
	 * Checks whether an exception is due to a deadlock error from Oracle.
	 * 
	 * @param th
	 *            base exception, can contain nexted exceptions
	 * @return true if the exceptions contains an oracle deadlock error
	 *         (ORA-00060)
	 */
	public static boolean isOracleDeadlock(Throwable th) {
		String oracleMessage = OracleHelper.searchOracleErrorMessage(th);
		return StringUtils.hasText(oracleMessage) && oracleMessage.contains("ORA-00060");
	}
}
